package com.minimalist.lindi.manager;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.minimalist.common.config.exception.BusinessException;
import com.minimalist.common.entity.basic.enums.FileEnum;
import com.minimalist.common.entity.basic.vo.file.FileUploadRespVO;
import com.minimalist.common.entity.basic.vo.file.FileVO;
import com.minimalist.common.entity.lindi.enums.LindiContractTemplateEnum;
import com.minimalist.common.entity.lindi.enums.LindiTradeApplicationEnum;
import com.minimalist.common.entity.lindi.po.LindiContractTemplate;
import com.minimalist.common.entity.lindi.po.LindiContractTemplateField;
import com.minimalist.common.entity.lindi.po.LindiTradeApplication;
import com.minimalist.common.entity.lindi.po.LindiTradeApplicationTemplateContract;
import com.minimalist.common.entity.lindi.vo.contracttemplate.LindiContractTemplateVO;
import com.minimalist.common.entity.lindi.vo.contracttemplatefield.LindiContractTemplateFieldVO;
import com.minimalist.common.feign.client.basic.FileFeignClient;
import com.minimalist.common.feign.encoder.ByteArrayMultipartFile;
import com.minimalist.common.response.R;
import com.minimalist.common.utils.PdfGenerate;
import com.minimalist.common.utils.UnqIdUtil;
import com.minimalist.lindi.mapper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author 11987
 */
@Component
public class ContractTemplateManager{

    private static final Logger log = LoggerFactory.getLogger(ContractTemplateManager.class);

    @Autowired
    LindiContractTemplateMapper lindiContractTemplateMapper;

    @Autowired
    private LindiContractTemplateFieldMapper lindiContractTemplateFieldMapper;

    @Autowired
    private LindiContractTemplateFieldOptionMapper lindiContractTemplateFieldOptionMapper;

    @Autowired
    private LindiTradeApplicationContractMapper lindiTradeApplicationContractMapper;

    @Autowired
    private LindiTradeApplicationMapper lindiTradeApplicationMapper;

    @Autowired
    private LindiTradeApplicationTemplateContractMapper lindiTradeApplicationTemplateContractMapper;

    @Autowired
    private FileFeignClient fileFeignClient;

    @Value("${file.upload.path:/temp}")
    private String uploadPath;

    @Transactional(rollbackFor = Exception.class)
    public void insertContractTemplate(LindiContractTemplateVO contractTemplateVO){
        LindiContractTemplate lindiContractTemplate = BeanUtil.copyProperties(
                contractTemplateVO, LindiContractTemplate.class);
        lindiContractTemplate.setContractTemplateId(UnqIdUtil.uniqueId());
        int insert = lindiContractTemplateMapper.insert(lindiContractTemplate);
        Assert.isTrue(insert == 1, () -> new BusinessException(
                LindiContractTemplateEnum.ErrorMsgEnum.ADD_CONTRACT_TEMPLATE_ERROR
                        .getDesc()));
        List<LindiContractTemplateFieldVO> fields = contractTemplateVO.getFields();
        if (!fields.isEmpty()){
            List<LindiContractTemplateField> fieldList = fields.stream().map(field -> {
                field.setContractTemplateId(lindiContractTemplate.getContractTemplateId());
                field.setContractTemplateFieldId(UnqIdUtil.uniqueId());
                return BeanUtil.copyProperties(field, LindiContractTemplateField.class);
            }).toList();
            int i = lindiContractTemplateFieldMapper.insertBatch(fieldList);
            Assert.isTrue(i == fieldList.size(), () -> new BusinessException(
                    LindiContractTemplateEnum.ErrorMsgEnum.ADD_CONTRACT_TEMPLATE_ERROR
                            .getDesc()));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateContractTemplate(LindiContractTemplateVO contractTemplateVO){
        LindiContractTemplate lindiContractTemplate = lindiContractTemplateMapper
                .selectContractTemplateByContractTemplateId(contractTemplateVO
                        .getContractTemplateId());
        Assert.notNull(lindiContractTemplate, () -> new BusinessException(
                LindiContractTemplateEnum.ErrorMsgEnum.CONTRACT_TEMPLATE_NOT_FOUND
                        .getDesc()));
        BeanUtil.copyProperties(contractTemplateVO, lindiContractTemplate);
        int update = lindiContractTemplateMapper.update(lindiContractTemplate);
        Assert.isTrue(update == 1, () -> new BusinessException(
                LindiContractTemplateEnum.ErrorMsgEnum.UPDATE_CONTRACT_TEMPLATE_ERROR
                        .getDesc()));
        lindiContractTemplateFieldMapper.deleteContractTemplateFieldByContractTemplateId(
                lindiContractTemplate.getContractTemplateId());
        List<LindiContractTemplateFieldVO> fields = contractTemplateVO.getFields();
        if (!fields.isEmpty()){
            List<LindiContractTemplateField> fieldList = fields.stream().map(field -> {
                field.setContractTemplateId(lindiContractTemplate.getContractTemplateId());
                field.setContractTemplateFieldId(UnqIdUtil.uniqueId());
                return BeanUtil.copyProperties(field, LindiContractTemplateField.class);
            }).toList();
            int i = lindiContractTemplateFieldMapper.insertBatch(fieldList);
            Assert.isTrue(i == fieldList.size(), () -> new BusinessException(
                    LindiContractTemplateEnum.ErrorMsgEnum.ADD_CONTRACT_TEMPLATE_ERROR
                            .getDesc()));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteContractTemplate(Long contractTemplateId){
        LindiContractTemplate lindiContractTemplate = lindiContractTemplateMapper
                .selectContractTemplateByContractTemplateId(contractTemplateId);
        Assert.notNull(lindiContractTemplate, () -> new BusinessException(
                LindiContractTemplateEnum.ErrorMsgEnum.CONTRACT_TEMPLATE_NOT_FOUND
                        .getDesc()));
        lindiContractTemplateFieldMapper.deleteContractTemplateFieldByContractTemplateId
                (contractTemplateId);
        R<Void> voidR = fileFeignClient.deleteFile(lindiContractTemplate.getContractTemplateFileId());
        Assert.isTrue(voidR.getCode() == HttpStatus.HTTP_OK,
                () -> new BusinessException(
                        LindiContractTemplateEnum.ErrorMsgEnum.DELETE_CONTRACT_TEMPLATE_FILE_ERROR
                                .getDesc()));
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteContractTemplateFileByFileId(Long fileId){
        // 调用文件服务删除文件
        R<Void> voidR = fileFeignClient.deleteFile(fileId);
        Assert.isTrue(voidR.getCode() == HttpStatus.HTTP_OK,
                () -> new BusinessException(
                        LindiContractTemplateEnum.ErrorMsgEnum.DELETE_CONTRACT_TEMPLATE_FILE_ERROR
                                .getDesc()));
        log.info("成功删除合同模板文件，文件ID: {}", fileId);
    }

    public LindiContractTemplateVO getContractTemplate(Long contractTemplateId){
        LindiContractTemplate lindiContractTemplate = lindiContractTemplateMapper
                .selectContractTemplateByContractTemplateId(contractTemplateId);
        Assert.notNull(lindiContractTemplate, () -> new BusinessException(
                LindiContractTemplateEnum.ErrorMsgEnum.CONTRACT_TEMPLATE_NOT_FOUND
                        .getDesc()));
        LindiContractTemplateVO lindiContractTemplateVO = BeanUtil.copyProperties(
                lindiContractTemplate, LindiContractTemplateVO.class);
        // 查询关联的文件信息
        if (lindiContractTemplate.getContractTemplateFileId() != null){
            R<FileVO> fileResult = fileFeignClient.getFileByFileId(
                    lindiContractTemplate.getContractTemplateFileId());
            if (fileResult.getCode() == HttpStatus.HTTP_OK
                    && fileResult.getData() != null){
                lindiContractTemplateVO.setContractTemplateFile(fileResult.getData());
            }
        }
        List<LindiContractTemplateField> fieldList = lindiContractTemplateFieldMapper
                .selectContractTemplateFieldListByContractTemplateId(contractTemplateId);
        List<LindiContractTemplateFieldVO> lindiContractTemplateFieldVOList =
                BeanUtil.copyToList(fieldList, LindiContractTemplateFieldVO.class);
        lindiContractTemplateVO.setFields(lindiContractTemplateFieldVOList);
        return lindiContractTemplateVO;
    }

    public List<LindiContractTemplateVO> getContractTemplateList(){
        List<LindiContractTemplate> lindiContractTemplates =
                lindiContractTemplateMapper
                        .selectAllEnableContractTemplateList();
        List<LindiContractTemplateVO> lindiContractTemplateVOList
                = BeanUtil.copyToList(lindiContractTemplates
                , LindiContractTemplateVO.class);

        // 收集所有需要查询的文件ID
        List<Long> fileIds = lindiContractTemplateVOList.stream()
                .map(LindiContractTemplateVO::getContractTemplateFileId)
                .filter(Objects::nonNull)
                .distinct()
                .toList();

        // 批量获取文件信息
        if (!fileIds.isEmpty()){
            R<List<FileVO>> fileListResult = fileFeignClient
                    .getFileListByFileIds(fileIds);
            if (fileListResult.getCode() == HttpStatus.HTTP_OK
                    && fileListResult.getData() != null){
                List<FileVO> fileList = fileListResult.getData();

                // 创建文件ID到文件信息的映射
                Map<Long, FileVO> fileMap = fileList.stream()
                        .collect(Collectors.toMap(FileVO::getFileId
                                , file -> file));

                // 为每个合同模板设置对应的文件信息
                for (LindiContractTemplateVO templateVO : lindiContractTemplateVOList){
                    if (templateVO.getContractTemplateFileId() != null){
                        FileVO fileVO = fileMap.get(templateVO
                                .getContractTemplateFileId());
                        if (fileVO != null){
                            templateVO.setContractTemplateFile(fileVO);
                        }
                    }
                }
            }
        }

        return lindiContractTemplateVOList;
    }

    @Transactional(rollbackFor = Exception.class)
    public R<FileUploadRespVO> generateContract(Long tradeApplicationId, Long contractTemplateId
            , String contractType, Long createId, Long tenantId){
        // 1. 验证交易申请是否存在
        LindiTradeApplication tradeApplication = lindiTradeApplicationMapper
                .selectTradeApplicationByTradeApplicationId(tradeApplicationId);
        Assert.notNull(tradeApplication, () -> new BusinessException(
                LindiTradeApplicationEnum.ErrorMsgEnum.NONENTITY_TRADE_APPLICATION
                        .getDesc()));

        List<LindiTradeApplicationTemplateContract> lindiTradeApplicationTemplateContracts
                = lindiTradeApplicationTemplateContractMapper
                .selectTradeApplicationTemplateContractByTradeApplicationId(
                        tradeApplicationId);
        List<LindiTradeApplicationTemplateContract> list =
                lindiTradeApplicationTemplateContracts.stream().filter(
                        p -> Objects.equals(p.getContractType()
                                , contractType)).toList();
        Assert.isTrue(list.isEmpty(), () -> new BusinessException("已存在合同模板，请先删除"));


        // 2. 验证合同模板是否存在
        LindiContractTemplate lindiContractTemplate = lindiContractTemplateMapper
                .selectContractTemplateByContractTemplateId(contractTemplateId);
        Assert.notNull(lindiContractTemplate, () -> new BusinessException(
                LindiContractTemplateEnum.ErrorMsgEnum.CONTRACT_TEMPLATE_NOT_FOUND
                        .getDesc()));
        Assert.isTrue(lindiContractTemplate.getStatus() == 1, () -> new BusinessException(
                LindiContractTemplateEnum.ErrorMsgEnum.CONTRACT_TEMPLATE_DISABLED
                        .getDesc()));

        // 3. 获取合同模板字段配置
        List<LindiContractTemplateField> lindiContractTemplateFields =
                lindiContractTemplateFieldMapper
                        .selectContractTemplateFieldListByContractTemplateId(
                                contractTemplateId);

        // 4. 获取合同模板文件
        R<FileVO> templateFileResult = fileFeignClient.getFileByFileId(
                lindiContractTemplate.getContractTemplateFileId());
        Assert.isTrue(templateFileResult.getCode() == HttpStatus.HTTP_OK
                        && templateFileResult.getData() != null,
                () -> new BusinessException("获取合同模板文件失败"));

        FileVO templateFile = templateFileResult.getData();
        String templateFilePath = templateFile.getFileUrl();

        // 5. 构建字段填充数据
        Map<String, String> fillData = buildFillData(
                tradeApplication, lindiContractTemplateFields);
        log.info("交易申请信息：{}", tradeApplication);
        log.info("模板字段信息：{}", lindiContractTemplateFields);
        log.info("字段填充数据：{}", fillData);

        // 6. 生成合同文件
        String contractFileName = generateContractFileName(
                tradeApplication, lindiContractTemplate);
        String outputPath = uploadPath + File.separator + contractFileName;

        // 使用表单填充PDF
        boolean success = PdfGenerate.fillPdfFormFields(templateFilePath, outputPath, fillData);

        Assert.isTrue(success, () -> new BusinessException("PDF合同生成失败"));

        // 7. 上传生成的合同文件
        File contractFileToUpload = new File(outputPath);
        ByteArrayMultipartFile multipartFile = new ByteArrayMultipartFile(
                contractFileToUpload);
        R<FileUploadRespVO> uploadResult = fileFeignClient.uploadFile(
                multipartFile,
                FileEnum.FileSource.LINDI_CONTRACT_TEMPLATE_FILE
                        .getCode(), // fileSource
                createId,
                createId,
                tenantId
        );
        Assert.isTrue(uploadResult.getCode() == HttpStatus.HTTP_OK
                        && uploadResult.getData() != null,
                () -> new BusinessException(FileEnum.ErrorMsg.FILE_UPLOAD_FAIL
                        .getDesc()));

        FileUploadRespVO contractFile = uploadResult.getData();

        // 9. 清理临时文件
        if (!contractFileToUpload.delete()){
            log.warn("临时文件删除失败: {}", outputPath);
        }

        return R.ok(contractFile);
    }

    /**
     * 构建字段填充数据
     */
    private Map<String, String> buildFillData(LindiTradeApplication tradeApplication
            , List<LindiContractTemplateField> fields){
        Map<String, String> fillData = new HashMap<>(16);

        // 将对象转换为JSON
        JSONObject tradeApplicationJson = JSONUtil.parseObj(tradeApplication);

        for (LindiContractTemplateField field : fields){
            String fieldKey = field.getFieldKey();
            String fieldValue = getFieldValueFromJson(tradeApplicationJson, fieldKey);

            if (StringUtils.hasText(fieldValue)){
                // 使用文本域名称或字段标识作为key
                String key = StringUtils.hasText(field.getTextFieldName())
                        ? field.getTextFieldName() : fieldKey;
                fillData.put(key, fieldValue);
            } else if (StringUtils.hasText(field.getDefaultValue())){
                // 使用默认值
                String key = StringUtils.hasText(field.getTextFieldName())
                        ? field.getTextFieldName() : fieldKey;
                fillData.put(key, field.getDefaultValue());
            }
        }

        return fillData;
    }

    /**
     * 通过JSON获取字段值
     */
    private String getFieldValueFromJson(JSONObject jsonObject, String fieldKey){
        try{
            Object value = jsonObject.get(fieldKey);
            return value != null ? value.toString() : null;
        } catch (Exception e){
            log.warn("获取字段值失败，字段名: {}, 错误: {}", fieldKey, e.getMessage());
            return null;
        }
    }

    /**
     * 生成合同文件名
     */
    private String generateContractFileName(
            LindiTradeApplication tradeApplication,
            LindiContractTemplate template){
        String timestamp = LocalDateTime.now().format(DateTimeFormatter
                .ofPattern("yyyyMMddHHmmss"));
        return String.format("%s_%s_%s.pdf",
                template.getContractTemplateName(),
                tradeApplication.getProjectName(),
                timestamp);
    }


    /**
     * 生成合同编号
     */
    private String generateContractNumber(){
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        return "CONTRACT_" + timestamp + "_" + UnqIdUtil.uniqueId();
    }
}
